package locationmanager

import (
	"context"
	"errors"
	"net/http"
	"sync"

	"dev.ixmxm.top/rms/boot"
	"dev.ixmxm.top/rms/codes"
	"dev.ixmxm.top/rms/model"
	"dev.ixmxm.top/rms/utils/function"
	"dev.ixmxm.top/rms/utils/log"
	"dev.ixmxm.top/rms/utils/tools"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

func New(db *boot.MongoDB) *LocationManager {
	wmm := &LocationManager{
		db: db,
	}
	wmm.httpClient = http.DefaultClient
	wmm.groundLocationGroupMap = make(map[string]*model.GroundLocation, 0)
	//初始换电梯缓存数据
	_ = wmm.LoadAllGroundLocation(context.Background())
	return wmm
}

type LocationManager struct {
	httpClient                *http.Client
	groundLocationGroupMap    map[string]*model.GroundLocation //内存中的电梯数
	groundLocationGroupMutex  sync.RWMutex                     //电梯互斥锁
	operateMuttex             sync.Mutex
	db                        *boot.MongoDB       //数据库对象
	SystemConfig              *model.SystemConfig //系统配置(缓存),不可直接赋值保存，调用SaveSystemConfigToCache保存
	taskStatusUpdateChan      chan model.Task
	initLocationGroupCallBack func(mapName string, locations []*model.Location)
}

func (wmm *LocationManager) SetInitLocationGroupCallBack(initCallback func(mapName string, locations []*model.Location)) {
	wmm.initLocationGroupCallBack = initCallback
}

//根据地图名称找出所有的地堆库组集合
func (wmm *LocationManager) GetGroundLocationsFromCacheByMapName(ctx context.Context, map_name string) []*model.Location {
	wmm.groundLocationGroupMutex.Lock()
	defer wmm.groundLocationGroupMutex.Unlock()
	if _, ok := wmm.groundLocationGroupMap[map_name]; ok {
		return wmm.groundLocationGroupMap[map_name].LocationData
	}
	return nil
}

//根据地图名称和点位，找出所在的地堆库
func (wmm *LocationManager) FindGroundLocation(map_name string, locationSiteId string) *model.Location {
	isContain := func(all []string, cur string) bool {
		for _, va := range all {
			if va == cur {
				return true
			}
		}
		return false
	}
	wmm.groundLocationGroupMutex.Lock()
	defer wmm.groundLocationGroupMutex.Unlock()
	if value, ok := wmm.groundLocationGroupMap[map_name]; ok {
		//遍历找出
		for _, groupObj := range value.LocationData {
			if isContain(groupObj.DepthPointId, locationSiteId) {
				return groupObj
			}

		}
	}
	return nil
}

//根据地图名称和点位，判断是否在同一列地堆库
func (wmm *LocationManager) CheckSameGroundLocation(map_name string, locationSite, locationSite1 model.Node) bool {
	if locationSite.Type != model.NodeStation || locationSite.LocationType != model.LocationGround {
		return false
	}
	if locationSite1.Type != model.NodeStation || locationSite1.LocationType != model.LocationGround {
		return false
	}
	wmm.groundLocationGroupMutex.Lock()
	defer wmm.groundLocationGroupMutex.Unlock()
	if value, ok := wmm.groundLocationGroupMap[map_name]; ok {
		//遍历找出
		for _, groupObj := range value.LocationData {
			in := tools.StringSliceIsContain(groupObj.DepthPointId, locationSite.Id)
			in1 := tools.StringSliceIsContain(groupObj.DepthPointId, locationSite1.Id)
			if in || in1 {
				if in && in1 {
					return true
				}
				return false
			}
		}
	}
	return false
}

//根据地图名和组名，查找单独location对象
func (wmm *LocationManager) GetGroundLocationFromCacheByMapNameAndGroupId(ctx context.Context, map_name, groupId string) *model.Location {
	wmm.groundLocationGroupMutex.Lock()
	defer wmm.groundLocationGroupMutex.Unlock()
	if value, ok := wmm.groundLocationGroupMap[map_name]; ok {
		//遍历，找出指定的group对象

		for _, groupObj := range value.LocationData {
			if groupObj.Group == groupId {
				return groupObj
			}
		}
	}
	return nil
}

//*********缓存相关***************
//loadAllGroundGroup 添加所有的地堆库组缓存
func (wmm *LocationManager) LoadAllGroundLocation(ctx context.Context) codes.Code {
	allGroundLocation, err := wmm.GetAllGroundLocations(ctx)
	if err != nil {
		log.ErrorfWithContext(ctx, "load ground location group to cache error:%s", err.UnWrapError())
		return err
	}
	wmm.groundLocationGroupMutex.Lock()
	defer wmm.groundLocationGroupMutex.Unlock()
	for _, groupLocation := range allGroundLocation {
		wmm.groundLocationGroupMap[groupLocation.MapName] = groupLocation
	}
	return nil
}

//删除所有地堆库，指定地图
func (wmm *LocationManager) DeleteAllGroudLocationToDB(ctx context.Context, mapName string) codes.Code {
	wmm.operateMuttex.Lock()
	defer wmm.operateMuttex.Unlock()

	_, err := wmm.db.Collection(ctx, model.GroundLocation{}.TableName()).DeleteOne(ctx, bson.M{"map_name": mapName})
	if err != nil {
		log.ErrorfWithContext(ctx, "delete all ground location groups from db error:%s", err.Error())
		return codes.ErrDatabase.Wrap(err)
	}

	wmm.setLocationGroupCatch(mapName, nil)
	// wmm.groundLocationGroupMutex.Lock()
	// //更新当前缓存中的地堆库数据
	// delete(wmm.groundLocationGroupMap, mapName)
	// if wmm.initLocationGroupCallBack != nil {
	// 	wmm.initLocationGroupCallBack(mapName, make([]*model.Location, 0))
	// }
	// wmm.groundLocationGroupMutex.Unlock()

	return nil
}

//删除单个指定地图和库位
func (wmm *LocationManager) DeleteOneGroudLocationToDB(ctx context.Context, mapName, groupId string) codes.Code {
	wmm.operateMuttex.Lock()
	defer wmm.operateMuttex.Unlock()
	//先获得所有当前地图的所有地堆库组
	// ctxTx.Rollback()
	//1 先查询该地图数据
	res1 := wmm.db.Collection(ctx, model.GroundLocation{}.TableName()).FindOne(ctx, bson.M{"map_name": mapName})
	if res1.Err() != nil && !errors.Is(res1.Err(), mongo.ErrNoDocuments) {
		log.ErrorfWithContext(ctx, "get location from db error:%s", res1.Err().Error())
		return codes.ErrDatabase.Wrap(res1.Err())
	}
	if errors.Is(res1.Err(), mongo.ErrNoDocuments) {
		wmm.setLocationGroupCatch(mapName, nil)
		return nil
	}
	//2.能查到值
	groundLocation1 := new(model.GroundLocation)
	err := res1.Decode(groundLocation1)
	if err != nil {
		log.ErrorfWithContext(ctx, "decode location error:%s", err.Error())
		return codes.ErrDatabase.Wrap(err)
	}
	if groundLocation1.LocationData == nil {
		wmm.setLocationGroupCatch(mapName, nil)
		return nil
	}
	//出现冲突，报错不更新,判断已存在的编号
	if groundLocation1.LocationData != nil {
		findIndex := -1
		for index, obj := range groundLocation1.LocationData {
			if obj.Group == groupId {
				findIndex = index
				break
			}
		}
		if findIndex != -1 {
			groundLocation1.LocationData = append(groundLocation1.LocationData[:findIndex], groundLocation1.LocationData[findIndex+1:]...)
		}
	}
	dbRes := wmm.db.Collection(ctx, model.GroundLocation{}.TableName()).FindOneAndReplace(ctx, bson.M{"map_name": groundLocation1.MapName}, groundLocation1, &options.FindOneAndReplaceOptions{Upsert: function.BoolPtr(true)})
	if dbRes.Err() != nil && !errors.Is(dbRes.Err(), mongo.ErrNoDocuments) {
		log.ErrorfWithContext(ctx, "find or add ground location from db error:%s", dbRes.Err().Error())
		return codes.ErrDatabase.Wrap(dbRes.Err())
	}
	wmm.setLocationGroupCatch(mapName, groundLocation1)
	// wmm.groundLocationGroupMutex.Lock()
	// //更新当前缓存中的地堆库数据
	// wmm.groundLocationGroupMap[mapName] = groundLocation1
	// if wmm.initLocationGroupCallBack != nil {
	// 	wmm.initLocationGroupCallBack(mapName, groundLocation1.LocationData)
	// }
	// wmm.groundLocationGroupMutex.Unlock()

	return nil
}

//修改单个地堆库组
func (wmm *LocationManager) UpdateGroudLocationToDB(ctx context.Context, oneLocation *model.OneGroundLocation) codes.Code {
	wmm.operateMuttex.Lock()
	defer wmm.operateMuttex.Unlock()
	//先获得所有当前地图的所有地堆库组
	// ctxTx.Rollback()
	//1 先查询该地图数据
	res1 := wmm.db.Collection(ctx, model.GroundLocation{}.TableName()).FindOne(ctx, bson.M{"map_name": oneLocation.MapName})
	if res1.Err() != nil && !errors.Is(res1.Err(), mongo.ErrNoDocuments) {
		log.ErrorfWithContext(ctx, "get location from db error:%s", res1.Err().Error())
		return codes.ErrDatabase.Wrap(res1.Err())
	}

	//2.能查到值
	groundLocation1 := new(model.GroundLocation)
	err := res1.Decode(groundLocation1)
	if err != nil {
		log.ErrorfWithContext(ctx, "decode location error:%s", err.Error())
		return codes.ErrDatabase.Wrap(err)
	}

	//出现冲突，报错不更新,判断已存在的编号

	if groundLocation1.LocationData != nil {
		for index, obj := range groundLocation1.LocationData {
			if obj.Group == oneLocation.Location.Group {
				groundLocation1.LocationData[index] = &oneLocation.Location

			}
		}
	} else {
		groundLocation1.LocationData = make([]*model.Location, 0)
		groundLocation1.LocationData = append(groundLocation1.LocationData, &oneLocation.Location)
	}

	dbRes := wmm.db.Collection(ctx, model.GroundLocation{}.TableName()).FindOneAndReplace(ctx, bson.M{"map_name": groundLocation1.MapName}, groundLocation1, &options.FindOneAndReplaceOptions{Upsert: function.BoolPtr(true)})
	if dbRes.Err() != nil && !errors.Is(dbRes.Err(), mongo.ErrNoDocuments) {
		log.ErrorfWithContext(ctx, "find or add ground location from db error:%s", dbRes.Err().Error())
		return codes.ErrDatabase.Wrap(dbRes.Err())
	}
	wmm.setLocationGroupCatch(oneLocation.MapName, groundLocation1)
	// wmm.groundLocationGroupMutex.Lock()
	// //更新当前缓存中的地堆库数据
	// wmm.groundLocationGroupMap[oneLocation.MapName] = groundLocation1
	// if wmm.initLocationGroupCallBack != nil {
	// 	wmm.initLocationGroupCallBack(oneLocation.MapName, groundLocation1.LocationData)
	// }
	// wmm.groundLocationGroupMutex.Unlock()

	return nil
}

//-------------------------------------------------------------------------------------------------------
//SaveGroundLocationToDB 存储地堆库数据到db
func (wmm *LocationManager) SaveGroundLocationToDB(ctx context.Context, groundLocation *model.GroundLocation) codes.Code {
	wmm.operateMuttex.Lock()
	defer wmm.operateMuttex.Unlock()
	//先获得所有当前地图的所有地堆库组
	// ctxTx := boot.Begin(ctx)
	// ctxTx.Rollback()
	//1 先查询该地图数据
	res1 := wmm.db.Collection(ctx, model.GroundLocation{}.TableName()).FindOne(ctx, bson.M{"map_name": groundLocation.MapName})
	if res1.Err() != nil && !errors.Is(res1.Err(), mongo.ErrNoDocuments) {
		log.ErrorfWithContext(ctx, "get location from db error:%s", res1.Err().Error())
		return codes.ErrDatabase.Wrap(res1.Err())
	}
	//查到数据为空
	if errors.Is(res1.Err(), mongo.ErrNoDocuments) {
		dbRes := wmm.db.Collection(ctx, groundLocation.TableName()).FindOneAndReplace(ctx, bson.M{"map_name": groundLocation.MapName}, groundLocation, &options.FindOneAndReplaceOptions{Upsert: function.BoolPtr(true)})
		if dbRes.Err() != nil && !errors.Is(dbRes.Err(), mongo.ErrNoDocuments) {
			log.ErrorfWithContext(ctx, "find or add ground location from db error:%s", dbRes.Err().Error())
			return codes.ErrDatabase.Wrap(dbRes.Err())
		}
		wmm.setLocationGroupCatch(groundLocation.MapName, groundLocation)
		return nil
	}

	//2.能查到值
	groundLocation1 := new(model.GroundLocation)
	err := res1.Decode(groundLocation1)
	if err != nil {
		log.ErrorfWithContext(ctx, "decode location error:%s", err.Error())
		return codes.ErrDatabase.Wrap(err)
	}

	//不为空，按照上传的替换
	if groundLocation1.LocationData == nil || len(groundLocation1.LocationData) < 1 {
		//直接替换
		dbRes := wmm.db.Collection(ctx, groundLocation.TableName()).FindOneAndReplace(ctx, bson.M{"map_name": groundLocation.MapName}, groundLocation, &options.FindOneAndReplaceOptions{Upsert: function.BoolPtr(true)})
		if dbRes.Err() != nil && !errors.Is(dbRes.Err(), mongo.ErrNoDocuments) {
			log.ErrorfWithContext(ctx, "find or add ground location from db error:%s", dbRes.Err().Error())
			return codes.ErrDatabase.Wrap(dbRes.Err())
		}
		wmm.setLocationGroupCatch(groundLocation.MapName, groundLocation)
		return nil
	}
	//出现冲突，报错不更新,判断已存在的编号
	for _, obj := range groundLocation1.LocationData {
		for _, curObj := range groundLocation.LocationData {
			if obj.Group == curObj.Group {
				return codes.ErrExistGroupInCurrentMap.WithDetails("上传地堆库组:", obj.Group, "已经存在")
			}
		}
	}
	//不存在包含关系，直接拼接数组
	groundLocation1.LocationData = append(groundLocation1.LocationData, groundLocation.LocationData...)

	dbRes := wmm.db.Collection(ctx, groundLocation.TableName()).FindOneAndReplace(ctx, bson.M{"map_name": groundLocation1.MapName}, groundLocation1, &options.FindOneAndReplaceOptions{Upsert: function.BoolPtr(true)})
	if dbRes.Err() != nil && !errors.Is(dbRes.Err(), mongo.ErrNoDocuments) {
		log.ErrorfWithContext(ctx, "find or add ground location from db error:%s", dbRes.Err().Error())
		return codes.ErrDatabase.Wrap(dbRes.Err())
	}
	wmm.setLocationGroupCatch(groundLocation1.MapName, groundLocation1)
	return nil
}

func (wmm *LocationManager) setLocationGroupCatch(mapName string, groundLocation1 *model.GroundLocation) {
	wmm.groundLocationGroupMutex.Lock()
	if groundLocation1 == nil {
		if _, find := wmm.groundLocationGroupMap[mapName]; find {
			delete(wmm.groundLocationGroupMap, mapName)
		}
	} else {
		//更新当前缓存中的地堆库数据
		wmm.groundLocationGroupMap[mapName] = groundLocation1
	}
	if wmm.initLocationGroupCallBack != nil {
		wmm.initLocationGroupCallBack(mapName, groundLocation1.LocationData)
	}
	wmm.groundLocationGroupMutex.Unlock()
}

//GetAllGroundLocations 从数据库获得指定地图的地堆库数据
func (wmm *LocationManager) GetAllGroundLocationsByMapName(ctx context.Context, map_name string) (*model.GroundLocation, codes.Code) {
	res1 := wmm.db.Collection(ctx, model.GroundLocation{}.TableName()).FindOne(ctx, bson.M{"map_name": map_name})
	if res1.Err() != nil && !errors.Is(res1.Err(), mongo.ErrNoDocuments) {
		log.ErrorfWithContext(ctx, "get location from db error:%s", res1.Err().Error())
		return nil, codes.ErrDatabase.Wrap(res1.Err())
	}
	if errors.Is(res1.Err(), mongo.ErrNoDocuments) {
		return nil, nil
	}
	groundLocation1 := new(model.GroundLocation)
	err := res1.Decode(groundLocation1)
	if err != nil {
		log.ErrorfWithContext(ctx, "decode location error:%s", err.Error())
		return nil, codes.ErrDatabase.Wrap(err)
	}
	return groundLocation1, nil
}

//查询所有的地堆库信息
func (wmm *LocationManager) GetAllGroundLocations(ctx context.Context) ([]*model.GroundLocation, codes.Code) {
	locations := make([]*model.GroundLocation, 0)
	res, err := wmm.db.Collection(ctx, model.GroundLocation{}.TableName()).Find(ctx, bson.D{})
	if err != nil {
		log.ErrorfWithContext(ctx, "get all locations from db error:%s", err.Error())
		return nil, codes.ErrDatabase.Wrap(err)
	}
	if errBind := res.All(ctx, &locations); errBind != nil {
		log.ErrorfWithContext(ctx, "bind all locations error:%s", errBind.Error())
		return nil, codes.ErrDatabase.Wrap(errBind)
	}
	return locations, nil
}
